# coding: utf-8
# @Author: cyl
# @File: 07async异步.py
# @Time: 2024/08/29 20:32:28
import time
import asyncio


"""
进程: 车厢
线程: 每个车厢的每个人干的事情不一样
协程: 再把每个人细分, 做的什么工作, 进行分类

衍生出异步的概念, 一个程序想要快速的访问没有卡顿问题: https://www.cnblogs.com/yoyoketang/p/16256696.html
"""


"""
python的异步就是一个event loop(事件循环): coroutine function、coroutine object、task
coroutine function: async def xxx()
coroutine object: coroutine function中的代码块
"""

async def sleep():
    print("hello")
    await asyncio.sleep(3)
    print("world")


async def eat():
    await asyncio.sleep(5)
    print("python")


"""create_task"""
async def say_after(delay: int, something: str):
    await asyncio.sleep(delay)
    return f"{something}- {delay}"


async def forRange(delay: int):
    sum: int = 2
    for i in range(delay):
        sum *= i
    return sum


async def study(delay: int):
    res: int = await forRange(delay=delay)
    print("res:", res)
    return res


async def main1():
    """
      1. create_task内部逻辑:
          1. 将coroutine object转换成一个task
          2. 将task注册到event_loop中
      2. 当函数执行到task1时候, 并没有办法去执行task1中的代码块, 此时控制权仍然在main中, 这个时候
         会继续将task2加入到event_loop
      3. 当await后面是一个task时候, 就省略了将continue转换为task的步骤, 只是告诉了event_loop
         我们需要这个task完成, 把控制权交还, 且在控制权回来的时候, 从task里面提取需要的返回值, 
      4. 当运行程序时候, 我们await task1时候, 此时event_loop中有三个task: main task1 task2, 
         当task1跟event_loop说我需要1s才能执行完成, 这时event_loop空闲, 会发现有一个task2可以执行, 
         于是又执行了task2, task2跟event_loop说我需要1s才能执行完成
      5. 这样两个task就是同时在执行(等待), 这也就是为什么asyncio适合处理网络请求io的问题, 因为
         网络请求都是在等待的过程中
      6. 本质上asyncio运行时候仍然是一段代码在执行, 只不过是在想办法减少程序在运行时的等待时间罢了
    """
    print("start time: %s" % time.strftime("%X"))
    task1 = asyncio.create_task(say_after(3, "hello"))
    task2 = asyncio.create_task(say_after(5, "world"))
    res1 = await task1
    res2 = await task2
    print("res1:", res1)
    print("res2:", res2)
    print("end time: %s" % time.strftime("%X"))


async def main2():
    print("="*30)
    print("start time: %s" % time.strftime("%X"))
    task1 = asyncio.create_task(say_after(1, "hello"))  
    task2 = asyncio.create_task(say_after(2, "world"))
    """
      gather函数:
         a. 不是coroutine, 而是返回一个future, 这个future也是可以await的
         b. 参数可以为若干个coroutine或者task, 两个都可以, 甚至可以是future, 也就是gather的return值
            还可以接着gather
         c. 如果传入的是coroutine, 首先会把它包装成task, 并且加入到event_loop中, 
            然后他会返回一个future值, 当去await这个future时, 也就是相当于告诉了event_loop, 
            我需要等待这里面的每一个task完成才会继续执行, 同时将这些task的值放到一个list中, 
            然后返回回来, list元素顺序和task的顺序是一致的
    """
    res = await asyncio.gather(task1, task2)
    print("res2:", res)
    print("end time: %s" % time.strftime("%X")) 


async def main3():
    print("=" * 30)
    print("start time: %s" % time.strftime("%X"))
    res = await asyncio.gather(
        say_after(1, "hello"),
        say_after(2, "world"),
        sleep(),
        study(100000)
    )
    print("res3:", res)
    print("end time: %s" % time.strftime("%X"))


if __name__ == "__main__":
    # 1 同步运行、异步运行
    # sleep()   # RuntimeWarning: Enable tracemalloc to get the object allocation traceback
    sl = sleep()

    # 2 create_task
    # asyncio.run(main1())
    
    # 3 gather
    # asyncio.run(main2())
    asyncio.run(main3())
